
namespace Microsoft.Sdc.Tasks.BizTalk2004.Configuration
{
    using System;
    using System.Management;
    using System.Xml;

    /// <summary>
    /// A send port.
    /// </summary>
    internal class SendPort : BizTalkConfigurableEntityBase
    {
		#region Member Variables

        private bool isDynamic;
        private bool isTwoWay;
        private TransportInfo primaryTransport;
        private TransportInfo secondaryTransport;
        private Pipeline sendPipeline;
        private Pipeline receivePipeline;
        private int priority = 5;
        private TrackingTypes trackingTypes;

        private FilterGroupCollection filterGroups = new FilterGroupCollection();
        private MapCollection outboundMaps = new MapCollection();
        private MapCollection inboundMaps = new MapCollection();

		#endregion

		#region Constructors

		/// <summary>
		/// Creates a new send port.
		/// </summary>
		/// <param name="installation">
		/// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkInstallation"/> that the send port will
		/// belong to.
		/// </param>
        public SendPort(BizTalkInstallation installation)
			: this(installation, null)
        {
        }

		/// <summary>
		/// Creates a new send port.
		/// </summary>
		/// <param name="installation">
		/// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkInstallation"/> that the send port will
		/// belong to.
		/// </param>
		/// <param name="name">
		/// The name of the send port.
		/// </param>
        public SendPort(BizTalkInstallation installation, string name)
			: base(installation, name)
        {
        }

		#endregion

		#region Properties

		/// <summary>
		/// Gets the collection of filter groups for the send port.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.FilterGroupCollection"/>.
		/// </value>
		public FilterGroupCollection Filters
		{
			get 
			{ 
				return this.filterGroups; 
			}
		}

		/// <summary>
		/// Gets the collection of inbound maps.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.MapCollection"/>.
		/// </value>
		public MapCollection InboundMaps
		{
			get 
			{ 
				return this.inboundMaps; 
			}
		}

		/// <summary>
		/// Gets or sets whether the port is isDynamic.
		/// </summary>
		/// <value>
		/// <b>true</b> if the port is isDynamic, or <b>false</b> if it is static.
		/// </value>
		public bool IsDynamic
		{
			get 
			{ 
				return this.isDynamic; 
			}
			set 
			{ 
				this.isDynamic = value; 
			}
		}

		/// <summary>
		/// Gets or sets whether the port is two way.
		/// </summary>
		/// <value>
		/// <b>true</b> if the port is two way, or <b>false</b> if it is one way.
		/// </value>
		public bool IsTwoWay
		{
			get 
			{ 
				return this.isTwoWay; 
			}
			set 
			{ 
				this.isTwoWay = value; 
			}
		}

        /// <summary>
        /// Gets the collection of outbound maps.
        /// </summary>
        /// <value>
        /// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.MapCollection"/>.
        /// </value>
        public MapCollection OutboundMaps
        {
            get 
			{ 
				return this.outboundMaps; 
			}
        }

		/// <summary>
		/// Gets the primary transport for the send port.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.TransportInfo"/>.
		/// </value>
		public TransportInfo PrimaryTransport
		{
			get 
			{ 
				//if there isn't a transport loaded we will create a new one
				if (this.primaryTransport == null)
				{
					this.primaryTransport = new TransportInfo(this, true);
				}
				return this.primaryTransport; 
			}
		}

		/// <summary>
		/// Gets or sets the priority of the send port.
		/// </summary>
		/// <value>
		/// A value between 1 and 10, with 1 being the highest priority. The default is 5.
		/// </value>
		public int Priority
		{
			get 
			{ 
				return this.priority; 
			}
			set 
			{ 
				if (value < 1 || value > 10)
				{
					throw new ArgumentOutOfRangeException("value", value, "The priority must be between 1 and 10.");
				}
				this.priority = value; 
			}
		}

		/// <summary>
		/// Gets or sets the receive pipeline.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.Pipeline"/>.
		/// </value>
		public Pipeline ReceivePipeline
		{
			get 
			{ 
				return this.receivePipeline; 
			}
			set 
			{ 
				this.receivePipeline = value; 
			}
		}

		/// <summary>
		/// Gets the secondary transport for the send port.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.TransportInfo"/>.
		/// </value>
		public TransportInfo SecondaryTransport
		{
			get 
			{ 
				//if there isn't a transport loaded we will create a new one
				if (this.secondaryTransport == null)
				{
					this.secondaryTransport = new TransportInfo(this, false);
				}
				return this.secondaryTransport; 
			}
		}

		/// <summary>
		/// Gets or sets the send pipeline.
		/// </summary>
		/// <value>
		/// A <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.Pipeline"/>.
		/// </value>
		public Pipeline SendPipeline
		{
			get 
			{ 
				return this.sendPipeline; 
			}
			set 
			{ 
				this.sendPipeline = value; 
			}
		}

		/// <summary>
		/// Gets or sets the tracking types for the send port.
		/// </summary>
		/// <value>
		/// A combination of the BtsTrackingTypes values.
		/// </value>
		public TrackingTypes TrackingType
		{
			get 
			{ 
				return this.trackingTypes; 
			}
			set 
			{ 
				this.trackingTypes = value; 
			}
		}

		#endregion

		#region Static Methods

		/// <summary>
		/// Deletes a send port from an installation.
		/// </summary>
		/// <param name="installation">
		/// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkInstallation"/> to delete the send
		/// port from.
		/// </param>
		/// <param name="name">
		/// The name of the send port.
		/// </param>
		public static void Delete(BizTalkInstallation installation, string name)
		{
			//check inputs
			if (installation == null)
			{
				throw new ArgumentNullException("installation");
			}
			if (name == null || name.Length == 0)
			{
				throw new ArgumentNullException("name");
			}

			//delete the send port group
			try
			{
				BtsSendPort port = installation.CatalogExplorer.SendPorts[name];
				port.Status = PortStatus.Bound;	//have to unenlist the send port before deleting
				installation.CatalogExplorer.RemoveSendPort(port);
				installation.CatalogExplorer.SaveChanges();
			}
			catch
			{
				installation.CatalogExplorer.DiscardChanges();
				throw;
			}
		}

		/// <summary>
		/// Loads a send port from an installation.
		/// </summary>
		/// <param name="installation">
		/// The <see cref="Microsoft.Sdc.Tasks.BizTalk2004.Configuration.BizTalkInstallation"/> to load the send
		/// port from.
		/// </param>
		/// <param name="name">
		/// The name of the send port.
		/// </param>
		public static SendPort Load(BizTalkInstallation installation, string name)
		{
			//check inputs
			if (installation == null)
			{
				throw new ArgumentNullException("installation");
			}
			if (name == null || name.Length == 0)
			{
				throw new ArgumentNullException("name");
			}

			//load the actual send port
			BtsSendPort actualPort = installation.CatalogExplorer.SendPorts[name];
			if (actualPort == null)
			{
				throw new ArgumentException("The send port does not exist.", "name");
			}

			//create a new config send port
			SendPort configPort = new SendPort(installation, name);
			configPort.trackingTypes = (TrackingTypes) Enum.Parse(typeof(TrackingTypes), ((int)actualPort.Tracking).ToString());
			configPort.priority = actualPort.Priority;
			configPort.isTwoWay = actualPort.IsTwoWay;
			configPort.isDynamic = actualPort.IsDynamic;
            
			//load pipelines
			BizTalkAssembly sendPipelineAssembly = installation.DeployedAssemblies[actualPort.SendPipeline.BtsAssembly.DisplayName];
			configPort.sendPipeline = sendPipelineAssembly.Pipelines[actualPort.SendPipeline.FullName];
			if (configPort.isTwoWay)
			{
				BizTalkAssembly receivePipelineAssembly = installation.DeployedAssemblies[actualPort.ReceivePipeline.BtsAssembly.DisplayName];
				configPort.receivePipeline = receivePipelineAssembly.Pipelines[actualPort.ReceivePipeline.FullName];
			}

			//load transports
			if (actualPort.PrimaryTransport != null && actualPort.PrimaryTransport.Address.Length > 0)
			{
				configPort.primaryTransport = TransportInfo.Load(configPort, true);
			}
			if (actualPort.SecondaryTransport != null && actualPort.SecondaryTransport.Address.Length > 0)
			{
				configPort.secondaryTransport = TransportInfo.Load(configPort, false);
			}

			//load filter
			if (actualPort.Filter != null && actualPort.Filter.Length > 0)
			{
				configPort.filterGroups.PopulateFromFilterXml(actualPort.Filter);
			}

			//load maps
			if (actualPort.OutboundTransforms != null)
			{
				foreach (object o in actualPort.OutboundTransforms)
				{
					BtsTransform actualMap = new BtsTransform(o, installation.CatalogExplorer);
					BizTalkAssembly mapAssembly = installation.DeployedAssemblies[actualMap.BtsAssembly.DisplayName];
					Map map = mapAssembly.Maps[actualMap.FullName];
					configPort.outboundMaps.Add(map);
				}
			}
			if (actualPort.InboundTransforms != null)
			{
				foreach (object o in actualPort.InboundTransforms)
				{
					BtsTransform actualMap = new BtsTransform(o, installation.CatalogExplorer);
					BizTalkAssembly mapAssembly = installation.DeployedAssemblies[actualMap.BtsAssembly.DisplayName];
					Map map = mapAssembly.Maps[actualMap.FullName];
					configPort.inboundMaps.Add(map);
				}
			}

			//set the entity as being loaded and return it
			configPort.Loaded = true;
			return configPort;
		}

		/// <summary>
		/// Checks if the Send Port exists within BizTalk
		/// </summary>
		/// <param name="installation">
		/// The Microsoft.Sdc.Tasks.Configuration.BizTalk.Installation to search in.
		/// </param>
		/// <param name="name">
		/// The name of the Send Port
		/// </param>
		/// <returns>
		/// <b>true</b> if the Send Port exists, or <b>false</b> otherwise.
		/// </returns>
		public static bool Exists(BizTalkInstallation installation, string name)
		{
			return Exists(installation, CollectionType.SendPort, name);
		}
		#endregion

		#region Methods

		/// <summary>
		/// Enlists the send port.
		/// </summary>
		public void Enlist()
		{
			this.Installation.CatalogExplorer.SendPorts[this.Name].Status = PortStatus.Stopped;
			this.Installation.CatalogExplorer.SaveChanges();
		}

		/// <summary>
		/// Starts the send port.
		/// </summary>
		public void Start()
		{
			this.Installation.CatalogExplorer.SendPorts[this.Name].Status = PortStatus.Started;
			this.Installation.CatalogExplorer.SaveChanges();
		}

		/// <summary>
		/// Stops the send port.
		/// </summary>
		public void Stop()
		{
			this.Installation.CatalogExplorer.SendPorts[this.Name].Status = PortStatus.Stopped;
			this.Installation.CatalogExplorer.SaveChanges();
		}

		/// <summary>
		/// Unenlists the send port.
		/// </summary>
		public void UnEnlist()
		{
			this.Installation.CatalogExplorer.SendPorts[this.Name].Status = PortStatus.Bound;
			this.Installation.CatalogExplorer.SaveChanges();
		}

		/// <summary>
		/// Saves this send port.
		/// </summary>
		/// <exception cref="System.InvalidOperationException">
		/// Thrown when this send port was loaded from an existing one which has been deleted, or when this
		/// send port is a new one and another one with the same name already exists.
		/// </exception>
		protected internal override void SaveImpl()
		{
			//get the actual send port
			BtsSendPort actualPort = this.Installation.CatalogExplorer.SendPorts[this.Name];
			if (actualPort == null)
			{
				if (this.Loaded)
				{
					throw new InvalidOperationException("The send port that this entity was loaded from no longer exists in the installation.");				
				}
				actualPort = this.Installation.CatalogExplorer.AddNewSendPort(this.isDynamic, this.isTwoWay);
			}	
			else
			{
				if (!this.Loaded)
				{
					throw new InvalidOperationException("A send port with the same name already exists in the installation.");
				}
			}

			//set the basic properties
			actualPort.Name = this.Name;
			actualPort.Priority = this.priority;
			actualPort.Tracking = (TrackingTypes)(int)this.trackingTypes;

			//save pipelines
			actualPort.SendPipeline = this.Installation.CatalogExplorer.Pipelines[this.sendPipeline.Name, this.sendPipeline.Assembly.Name];
			if (this.receivePipeline != null)
			{
				actualPort.ReceivePipeline = this.Installation.CatalogExplorer.Pipelines[this.receivePipeline.Name, this.receivePipeline.Assembly.Name];
			}

			//save transports
            if (this.primaryTransport != null)
            {
                this.primaryTransport.SaveImpl();
                if (this.secondaryTransport != null)
                {
                    this.secondaryTransport.SaveImpl();
                }
            }

			//save filter
			if (this.filterGroups != null && this.filterGroups.Count > 0)
			{
				actualPort.Filter = this.filterGroups.ToFilterXml();
			}
			else
			{
				actualPort.Filter = string.Empty;
			}
			
			//save maps
			if (actualPort.InboundTransforms != null)
			{
				actualPort.InboundTransforms.Clear();
			}

			if (this.inboundMaps != null)
			{
				foreach (Map map in this.inboundMaps)
				{
					BtsTransform actualMap = this.Installation.CatalogExplorer.Transforms[map.Name, map.Assembly.Name];
					actualPort.InboundTransforms.Add(actualMap.actualBtsObject);
				}
			}

			if (actualPort.OutboundTransforms != null)
			{
				actualPort.OutboundTransforms.Clear();
			}

			if (this.outboundMaps != null)
			{
				foreach (Map map in this.outboundMaps)
				{
					BtsTransform actualMap = this.Installation.CatalogExplorer.Transforms[map.Name, map.Assembly.Name];
					actualPort.OutboundTransforms.Add(actualMap.actualBtsObject);
				}
			}
		}

		#endregion
    }
}

